/**
 * @file Event.hh
 * @author Xiaoze Lin (linxiaoze96@gmail.com)
 * @brief The event class.
 * @version 0.1
 *
 * @copyright Copyright (c) 2024
 *
 */
#pragma once

#include <numa.h>

#include <iostream>
#include <queue>
#include <vector>

namespace ifsim {

// Virutal base class for event queue handling
class EventManager
{
 public:
  virtual void clear() = 0;

  virtual int next() = 0;  // Get gate id of next event;
                           // -1 means empty queue

  virtual void insert(int g, int level) = 0;  // Insert gate into event Q

  virtual int threadNextEvent(int tid) = 0;

  virtual void insertThreadEvent(int g, int level, int tid) = 0;

  virtual bool empty() const = 0;

  virtual int curLevel() const = 0;
  virtual void curLevel(int v) = 0;

  virtual int threadCurLevel(int tid) const = 0;
  virtual void threadCurLevel(int v, int tid) = 0;
};

class QueueBasedEventManager : public EventManager
{
 public:
  QueueBasedEventManager(int num_nodes, int num_thread, const std::vector<int>& firstNodePerLevel);
  QueueBasedEventManager();
  ~QueueBasedEventManager();

  void init(int num_nodes, int num_thread, const std::vector<int>& firstNodePerLevel);
  void clear();

  int next();  // Get gate id of next event;
               // -1 means empty queue

  void insert(int g, int level);  // Insert a gate into event list

  int threadNextEvent(int tid);

  void insertThreadEvent(int g, int level, int tid);  // Insert a gate into event list in different thread

  void initPtr(int num_nodes, int num_thread, const std::vector<int>& firstNodePerLevel);
  void allocDataStructuresToNumaNode(int thread_id, int curr_node_id, bool vec_mode, const std::vector<int>& firstNodePerLevel);
  void freeDataStructuresInNumaNode(bool vec_mode);

  bool empty() const { return curLevel() == -1; }

  int curLevel() const { return _curLevel; }
  void curLevel(int v) { _curLevel = v; }

  int threadCurLevel(int tid) const { return _threadCurLevel[tid]; }
  void threadCurLevel(int v, int tid) { _threadCurLevel[tid] = v; }

 protected:
  bool inQueue(int g) const { return _flagsPtr[g]; }
  int numLevels() const { return _numLevels; }
  int numThreads() const { return _numThread; }

 private:
  // One-dimensional event queue for all levels
  //  * its size is the same of netlist size
  //  * event queue for level 'l' starts at firstNodeInLevel(l)
  //  * _flags[n] of each node 'n' indicate if the node is in queue
  std::vector<int> _events;
  int* _eventsPtr;
  int** _eventsPPtr;
  std::vector<int> _flags;
  int* _flagsPtr;
  int** _flagsPPtr;
  std::vector<int> _queueOffsets;  // offset in _events for each level
  int* _qOffsetsPtr;
  int** _qOffsetsPPtr;
  std::vector<int> _queueSizes;  // queue size for each level
  int* _qSizesPtr;
  int** _qSizesPPtr;
  int _numNodes;
  int _numLevels;
  int _curLevel;  // -1: all event queues are empty (for 3-frame sim only)
                  // >=0: lowest level where event exists
                  //   _curLevel goes through 1, 2, .. n then 0

  std::vector<int> _threadCurLevel;  // curlevel of different thread
  int _numThread;
};
}  // namespace ifsim